#include <string>

//不会用
using  namespace std;
enum token {
    ID,
    INT,
    ASSIGN,
    SEMICOLON,
    PRINT,
    LPAREN,
    RPAREN,
    COMMA,
    PLUS,
    MINUS,
    TIMES,
    DIV
};

union tokenval {
    string id;
    int num;
};

enum token tok;
union tokenval tokval;
//tokenval tokval;
typedef struct table *Table_;
Table_ {
string id;
int value;
Table_ tail;
};
Table_ Table(string id, int value, struct table *tail);
Table_ table = NULL;

enum token  getToken(){

}
void error(){

}
void advance() { tok = getToken(); }
void eat(enum token t) {
    if (tok == t)
        advance();
    else
        error();
}
int lookup(Table_ table, string id) {
    assert(table != NULL);
    if (id == table.id)
        return table.value;
    else
        return lookup(table.tail, id);
}
void update(Table_ *tabptr, string id, int value) {
    *tabptr = Table(id, value, *tabptr);
}

// Assum that the tok's initial value is the first input token. If not, in
// Prog(), we can set it.

int Prog_follow[] = {};
int Prog() {
    switch (tok) {
        case ID:
        case PRINT: {
            return Stm();
        }
        default: {
            printf("Error: expect ID or PRINT!\n");
            return 0;
        }
    }
}

int Stm_follow[] = {SEMICOLON, COMMA};
int Stm() {
    switch (tok) {
        case ID: {  // tok is ID
            string id = tokval.id;
            if (lookahead() == ASSIGN) {
                advance();                  // tok is ASSIGN
                advance();                  // tok is exp's first token
                update(&table, id, Exp());  // update; semantic action

            } else {
                printf("Error: expect ASSIGN\n");
            }
            return StmPrime();
        }
        case PRINT: {  // tok is PRINT
            string id = tokval.id;
            if (lookahead() == LPAREN) {
                advance();     // tok is LPAREN
                advance();     // tok is exps's first token
                Exps();        // do exps
                printf("\n");  // semantic action
            } else {
                printf("expected ASSIGN");
            }
            eatOrSkipTo(RPAREN, Stm_follow);  // eat RPAREN

            return StmPrime();
        }
        default: {
            printf("Error: expect ID or PRINT!\n");
            skipto(Stm_follow);
            return 0;
        }
    }
}

int StmPrime_follow[] = {SEMICOLON, COMMA};
int StmPrime() {
    switch (tok) {
        case SEMICOLON: {  // tok is SEMICOLON
            SemiStm();       // do semi_stm which will change tok
            StmPrime();      // do stm'
            return 0;
        }
        case COMMA:
        default: {
            skipto(StmPrime_follow);
            return 0;
        }
    }
}

int SemiStm_follow = {SEMICOLON};
int SemiStm() {
    switch (tok) {
        case SEMICOLON: {
            advance();
            Stm();
            return 0;
        }
        default: {
            printf("Error: expect semicolon!\n");
            skipto(SemiStm_follow);
            return 0;
        }
    }
}

int Exps_follow = {RPAREN};
int Exps() {
    switch (tok) {
        case LPAREN:
        case PRINT:
        case ID:
        case INT: {
            printf("%d ", Exp());
            ExpsPrime();
        }
        default: {
            printf("Error: expect LPAREN,PRINT,ID or INT!\n");
            skipto(Exps_follow);
            return 0;
        }
    }
    return 0;
}

int ExpsPrime_follow[] = {RPAREN};
int ExpsPrime() {
    switch (tok) {
        case COMMA: {
            advance();  // tok is exp first token
            printf("%d ", Exp());
            ExpsPrime();
        }
        default: {
            skipto(ExpsPrime_follow);
            return 0;
        }
    }
    return 0;
}

int Exp_follow[] = {SEMICOLON, COMMA, RPAREN};
int Exp() {
    switch (tok) {
        case ID: {
            if (lookahead() == ASSIGN) {  // next token is ASSIGN, use stm production
                Stm();
                advance();  // comma
                int return_value =
                        ExpPrime(Exp());  // pass this value to exp' as a tricky iteration

                return return_value;  // semantic action

            } else {
                return ExpPrime(
                        Term());  // use term production and pass this value to exp'
            }
        }
        case INT: {
            return ExpPrime(
                    Term());  // use term production and pass this value to exp'
        }
        case PRINT: {
            Stm();
            advance();  // comma
            int return_value = Exp();
            ExpPrime();           // do exp'
            return return_value;  // semantic action
        }
        case LPAREN: {
            return Term();
        }
        default: {
            printf("Error: expect LPAREN,PRINT,ID or INT!\n");
            skipto(Exp_follow);
            return 0;
        }
    }
}

int ExpPrime_follow[] = {SEMICOLON, COMMA, RPAREN};
int ExpPrime(int a) {
    switch (tok) {
        case PLUS: {
            advance();  // tok is term first token
            return ExpPrime(a + Term());
        }
        case MINUS: {
            advance();  // tok is term first token
            return ExpPrime(a - Term());
        }
        default: {
            skipto(ExpPrime_follow);
            return a;
        }
    }
    return 0;
}

// 项
int Term_follow[] = {SEMICOLON, COMMA, RPAREN, PLUS, MINUS};
int Term() {
    switch (tok) {
        case ID:
        case INT:
        case LPAREN: {
            return TermPrime(
                    Factor());  // use factor production and pass this value to term'
        }
        default: {
            printf("Error: expect LPAREN,ID or INT!\n");
            skipto(Term_follow);
            return 0;
        }
    }
}

// 不分析fllow 集合的话 好像不行
int TermPrime_follow[] = {SEMICOLON, COMMA, RPAREN, PLUS, MINUS};
int TermPrime(int a) {
    switch (tok) {
        case TIMES: {
            advance();  // tok is factor first token
            return TermPrime(a * Factor());
        }
        case DIV: {
            advance();  // tok is factor first token
            return ExpPrime(a / Factor());
        }
        default: {
            skipto(TermPrime_follow);
            return a;
        }
    }
    return 0;
}

int factor_follow[] = {SEMICOLON, COMMA, RPAREN, PLUS, MINUS, TIMES, DIV};
int Factor() {
    switch (tok) {
        case ID: {
            return lookup(table, tokval.id);  // semantic action
        }
        case INT: {
            return tokval.num;  // semantic action
        }
        case LPAREN: {
            advance();  // tok is exp first token now
            int return_value = Exp();
            eatOrSkipTo(RPAREN, factor_follow);  // eat RPAREN
            return return_value;
        }
        default: {
            printf("Error: expect LPAREN,ID or INT!\n");
            skipto(Term_follow);
            return 0;
        }
    }
}

// https://blog.csdn.net/ken_for_learning/article/details/79852540

int main(){
    Term();
}